/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package secondround10;

import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.util.Scanner;
import java.io.IOException;
import java.io.PrintStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * @author robit
 */
public class Bacteria{
    /*
      All neccesary to be defined
      including:
      variable, inner class
    */
    // static int A;
    // static void B();
    // static class C
    public static Integer head[];
    public static Integer rank[];
    public static void initUnion(int size){
        head=new Integer[size];
        rank=new Integer[size];
        Arrays.fill(rank, 0);
        for(int i=0;i<size;i++){
            head[i]=i;
        }
    }

    public static int findhead(int a){
        int itr=a;
        while(head[itr]!=itr)
            itr=head[itr];
        final int root= itr;
        itr=a;
        while(head[itr]!=root){
            int tmp=head[itr];
            head[itr]=root;
            itr=tmp;
        }
        return root;
    }

    public static void unionset(int a, int b)
    {
        int heada = findhead(a);
        int headb=findhead(b);
        if(heada==headb)
            return;
        if(rank[heada]>rank[headb])
            head[headb]=heada;
        else if(rank[headb]>rank[heada])
            head[heada]=headb;
        else
        {
            head[headb]=heada;
            rank[heada]++;
        }
    }

    public static boolean intersect(int AX1, int AX2, int AY1, int AY2, int BX1,int BX2,int BY1, int BY2){
            return (Math.max(AX1, BX1)<=Math.min(AX2+1, BX2))&&(Math.max(AY1-1, BY1)<=Math.min(AY2, BY2))||
                   (Math.max(AX1, BX1)<=Math.min(AX2, BX2+1))&&(Math.max(AY1, BY1-1)<=Math.min(AY2, BY2));
    }
    
    /*
       One calculation fits all of the cases.
    */
    static void preReckon(){
        return;
    }

/*
      calculate the answer
    */
    private static int brute(boolean grid[][], int R) {
        int done=0;
        boolean alive;
        int ans=0;
        do{
           boolean buffer[][]=new boolean[250][250];
           alive=false;
           for(int x=1;x<250;x++)
           for(int y=1;y<250;y++)
           {
             if(grid[x-1][y]&&grid[x][y-1]) buffer[x][y]=true;
             else if(!grid[x-1][y]&&!grid[x][y-1]) buffer[x][y]=false;
             else buffer[x][y]=grid[x][y];
             alive|=buffer[x][y];
           }
           grid=buffer;
           ans++;
        }while(alive);

        return ans;
    }


    /*
      calculate the answer
    */
    private static void reckon(int line) {
        int R=sc.nextInt();
        int[] X1=new int[R], X2=new int[R], Y1=new int[R], Y2=new int[R];

        boolean[][] grid=new boolean[250][250];//to brute

        for(int i=0;i<R;i++){
           X1[i]=sc.nextInt();Y1[i]=sc.nextInt();
           X2[i]=sc.nextInt();Y2[i]=sc.nextInt();
//           for(int x=X1[i];x<=X2[i];x++)
//           for(int y=Y1[i];y<=Y2[i];y++)
//           {
//              grid[x][y]=true;
//           }
        }

        initUnion(R);
        for(int i=0;i<R;i++)
        for(int j=0;j<R;j++){
            if(intersect(X1[i], X2[i], Y1[i], Y2[i], X1[j], X2[j], Y1[j], Y2[j]))
            unionset(i, j);
        }

        int[] corner=new int[R], rightmost=new int[R], lowest=new int[R];
        Arrays.fill(corner, Integer.MAX_VALUE);
        for(int i=0;i<R;i++){
            int root=findhead(i);
            if(X1[i]+Y1[i]<corner[root])corner[root]=X1[i]+Y1[i];
            if(X2[i]>rightmost[root])rightmost[root]=X2[i];
            if(Y2[i]>lowest[root])lowest[root]=Y2[i];
        }

        int ans=0;
        for(int i=0;i<R;i++){
            if(i!=findhead(i))continue;
            ans=Math.max(ans,1+rightmost[i]+lowest[i]-corner[i]);
        }

        //sys.println(String.format("%d vs %d", ans, brute(grid, R)));

        //Arrays.sort(P);
        out.println(String.format("Case #%d: %d",line, ans));

        //anl.println(String.format("N=%d K=%d\n%s", N, K, ans));
        //println(output);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        /*
           Pre-computing for all cases
        */

        // Just in case!
        //preReckon();
        //while((char)read()!='P');

        /*
          Input-output
        */
        boolean test=false;
        boolean small=false;
        String prob="C";
        String filename;
        if(test) filename="test";
        else{
            if(small) filename=prob+"-small-practice";
            else filename=prob+"-large-practice";
        }
        try {
            /*
               Init IO,plus determination of input line number
             */
            initFileIO(filename);
            int lineNums=sc.nextInt();
            for (int i = 1; i <= lineNums; i++) {
                sys.println("Case"+i);
                reckon(i);                
            }
        } finally {
            endIO();
        }
    }

    private static Scanner sc = null;
    private static PrintWriter out,anl = null;
    private static PrintStream sys=System.out;
    public final static int OUTFILE=0,ANALFILE=1;

    public static void  initFileIO(String filename) throws IOException{
        sc = new Scanner(
                new BufferedReader(new FileReader(filename+".in")));
            //s.useLocale(Locale.US);
        out = new PrintWriter(
                new BufferedWriter(new FileWriter(filename+".out")));
        anl = new PrintWriter(
                new BufferedWriter(new FileWriter(filename+".anal")));

    }

    public static void endIO(){
        sc.close();
        out.close();
    }

}
